package com.system.core.conf;

import java.io.InputStream;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;

import com.alibaba.fastjson.JSONObject;
import com.system.core.conf.dto.ExceptionCommonRsp;
import com.system.core.message.GenericException;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;

@ControllerAdvice
@Slf4j
public class MyExceptionHandler {

	@ExceptionHandler(BindException.class)
	public ResponseEntity<Object> handleBindException(BindException ex, WebRequest request,HttpServletResponse response) {
		String error = "验证失败: " + ex.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage)
				.collect(Collectors.joining(", "));
		ExceptionCommonRsp resultBase = ExceptionCommonRsp.builder().message(error).causeErrorType("BindException").build();
		return handleExceptionInternal(ex, resultBase, request,response);
	}

	@ExceptionHandler({ DataIntegrityViolationException.class })
	public ResponseEntity<Object> handleDataIntegrityViolationException(Exception ex, WebRequest request,HttpServletResponse response) {
		ExceptionCommonRsp resultBase = ExceptionCommonRsp.builder().message("违反数据完整性异常，请检查数据").causeErrorType("DataIntegrityViolationException").build();
		return handleExceptionInternal(ex, resultBase, request,response);
	}

	@ExceptionHandler({ DuplicateKeyException.class })
	public ResponseEntity<Object> handleDuplicateKeyException(Exception ex, WebRequest request,HttpServletResponse response) {
		String msg = ex.getCause().getMessage();
		StringBuilder extendMsg = new StringBuilder();
		if (StringUtils.isNotEmpty(msg)) {
			Pattern pattern = Pattern.compile("Duplicate entry '(.*)' for key", Pattern.CASE_INSENSITIVE);
			Matcher m = pattern.matcher(msg);
			if (m.find()) {
				extendMsg.append("(");
				extendMsg.append(m.group(1));
				extendMsg.append(")");
			}
		}
		ExceptionCommonRsp resultBase = ExceptionCommonRsp.builder().message("保存数据重复，请检查数据。" + extendMsg).causeErrorType("DuplicateKeyException").build();
		return handleExceptionInternal(ex, resultBase, request,response);
	}

	@ExceptionHandler({ Exception.class })
	public ResponseEntity<Object> handleException(Exception ex, WebRequest request,HttpServletResponse response) {
		ExceptionCommonRsp resultBase = ExceptionCommonRsp.builder().message(ex.getMessage()).causeErrorType("Exception").build();
		return handleExceptionInternal(ex, resultBase, request,response);
	}
	
	@ExceptionHandler({ GenericException.class })
	public ResponseEntity<Object> handleGenericException(GenericException ex, WebRequest request,HttpServletResponse response) {
		ExceptionCommonRsp resultBase = ExceptionCommonRsp.builder().message(ex.getMessage()).causeErrorType("GenericException").build();
		return handleExceptionInternal(ex, resultBase, request,response);
	}
	
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public ResponseEntity<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex,
			WebRequest request,HttpServletResponse response) {
		BindingResult bindingResult = ((MethodArgumentNotValidException) ex).getBindingResult();
		StringBuilder messages = methodArgumentNotValidMessage(bindingResult);
		ExceptionCommonRsp resultBase = ExceptionCommonRsp.builder().message(messages.toString()).causeErrorType("MethodArgumentNotValidException").build();
		return handleExceptionInternal(ex, resultBase, request,response);
	}

	@ExceptionHandler({ RuntimeException.class })
	public ResponseEntity<Object> handleRuntimeException(RuntimeException ex, WebRequest request,HttpServletResponse response) {
		ExceptionCommonRsp resultBase = ExceptionCommonRsp.builder().message(ex.getMessage()).causeErrorType("RuntimeException").build();
		return handleExceptionInternal(ex, resultBase, request,response);
	}

	@ExceptionHandler({ SQLIntegrityConstraintViolationException.class })
	public ResponseEntity<Object> handleSQLIntegrityConstraintViolationException(Exception ex, WebRequest request,HttpServletResponse response) {
		ExceptionCommonRsp resultBase = ExceptionCommonRsp.builder().message("数据约束异常，请检查数据").causeErrorType("SQLIntegrityConstraintViolationException").build();
		return handleExceptionInternal(ex, resultBase, request,response);
	}

	private StringBuilder buildParams(HttpServletRequest httpServletRequest) {
		StringBuilder params = new StringBuilder("{");
		Enumeration<String> paramenum = httpServletRequest.getParameterNames();
		if (paramenum != null) {
			while (paramenum.hasMoreElements()) {
				String param = (String) paramenum.nextElement();
				params.append("'" + param + "'");
				params.append(" : ");
				Object paramValue = httpServletRequest.getParameter(param);
				if (paramValue == null) {
					params.append("null");
				} else if (NumberUtils.isCreatable(String.valueOf(paramValue))) {
					params.append(paramValue);
				} else {
					params.append("'" + paramValue + "'");
				}
			}
		}
		params.append("}");

		try (InputStream inputStream = httpServletRequest.getInputStream()) {
			String bodyparam = IOUtils.toString(inputStream, "utf-8");
			bodyparam = bodyparam.replaceAll("\n", "").replaceAll("\"", "'");
			if (bodyparam != null && bodyparam != "") {
				params.delete(0, params.length());
				params.append(" ");
				params.append(bodyparam);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return params;
	}

	private HttpStatus getStatus(WebRequest request) {
		Integer statusCode = (Integer) request.getAttribute("jakarta.servlet.error.status_code",
				RequestAttributes.SCOPE_REQUEST);
		if (statusCode == null) {
			return HttpStatus.INTERNAL_SERVER_ERROR;
		} else {
			return HttpStatus.valueOf(statusCode);
		}
	}

	private StringBuilder methodArgumentNotValidMessage(BindingResult bindingResult) {
		StringBuilder messages = new StringBuilder();
		List<ObjectError> errors = bindingResult.getAllErrors();
		for (int i = 0; i < errors.size(); i++) {
			if (i > 0) {
				messages.append("\n");
				messages.append(i + "、");
			}
			messages.append(errors.get(i).getDefaultMessage());
		}
		return messages;
	}

	private ResponseEntity<Object> handleExceptionInternal(Exception ex, Object result, WebRequest request,HttpServletResponse response) {
		HttpStatusCode httpStatusCode = getStatus(request);
		ServletWebRequest servletWebRequest = (ServletWebRequest) request;
		HttpServletRequest httpServletRequest = servletWebRequest.getRequest();
		response.setContentType("application/json;charset=UTF-8");
		String requri = httpServletRequest.getRequestURI();

		StringBuilder params = buildParams(httpServletRequest);

		log.error("[system]异常发生地址是：{}, 入参：{}, 异常打印如下：", requri, params.toString(), ex);
		ExceptionCommonRsp resultBase = (ExceptionCommonRsp) result;
		resultBase.setCode("500");
		resultBase.setRequestPath(requri);
		resultBase.setCauseParams(params.toString());
		resultBase.setCauseErrorType(resultBase.getCauseErrorType()+"->"+ex.getClass().getSimpleName());

		if (requri.startsWith("/")) {
			if (result != null) {
				if (!(ex instanceof BindException)) {
					resultBase.setMessage(resultBase.getMessage() == null ? ex.getMessage() : resultBase.getMessage());
				}
				return new ResponseEntity<Object>(JSONObject.toJSONString(resultBase), HttpStatus.OK);
			} else {
				/**
				 * 表单验证异常处理，取默认信息提供前端展示
				 */
				resultBase.setMessage(ex.getMessage());
				resultBase.setRequestPath(requri);
				return new ResponseEntity<>(JSONObject.toJSONString(resultBase), httpStatusCode);
			}
		} else {
			return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
		}
	}
}
